﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.RelyingParty;
using Mvc.Mailer;
using viBook.Common;
using viBook.Filters;
using viBook.Mailers;
using viBook.ModelBinders;
using viBook.Models;
using viBook.Helpers;
using viBook.Repositories;

namespace viBook.Controllers
{
    public class AccountController : BaseController
    {
        #region Constructors and Repository vars

        private readonly IActivityRepository _activityRepository;
        private readonly OpenIdRelyingParty _openId;
        private readonly IUserMailer _userMailer;
        private readonly IUserRepository _userRepository;

        public AccountController(IUserRepository userRepository, IActivityRepository activityRepository, IUserMailer userMailer)
        {
            _openId = new OpenIdRelyingParty();
            _userMailer = userMailer;
            _activityRepository = activityRepository;
            _userRepository = userRepository;
        }

        #endregion

        [ValidateInput(false)]
        public ActionResult Authenticate()
        {
            var response = _openId.GetResponse();
            //check the response
            if (response != null)
            {
                switch (response.Status)
                {
                    case AuthenticationStatus.Authenticated:
                        var fetch = response.GetExtension<FetchResponse>();
                        var email = string.Empty;
                        if (fetch != null)
                        {
                            email = fetch.GetAttributeValue(WellKnownAttributes.Contact.Email);
                        }
                        var user = _userRepository.FindByEmail(email);
                        _userRepository.Authenticate(email);
                        if (user == null)
                        {
                            var u = _userRepository.FindByEmail(email);
                            _activityRepository.CreateActivity(u, Constant.RegisteredType);
                            _activityRepository.Save();
                        }
                        if (user != null)
                        {
                            if (!user.IsExternal)
                            {
                                ViewBag.Message = Constant.EmailHasBeenRegisteredMessage;
                                return View("LogOn");    
                            }
                            if (user.IsBlocked)
                            {
                                ViewBag.Message = Constant.BlockedAccountMessage;
                                return View("LogOn");
                            }
                            
                        }
                        FormsAuthentication.SetAuthCookie(email, false);
                        SessionPersiter.User = user;
                        return RedirectToAction("Index", "Home");

                    case AuthenticationStatus.Canceled:
                        ViewBag.Message = Constant.CanceledAtProviderMessage;
                        return View("LogOn");

                    case AuthenticationStatus.Failed:
                        ViewBag.Message = response.Exception.Message;
                        return View("LogOn");
                }
            }
            return new EmptyResult();
        }

        [ValidateInput(false), HttpPost]
        public ActionResult Authenticate(string returnUrl, string provider)
        {
            var response = _openId.GetResponse();
            if (response == null)
            {
                Identifier id;
                if (Identifier.TryParse(provider, out id))
                {
                    try
                    {
                        var request = _openId.CreateRequest(id);

                        var fetch = new FetchRequest();
                        fetch.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
                        request.AddExtension(fetch);
                        return Json(new { Success = true, RedirectUrl = request.RedirectingResponse.Headers["Location"] });
                    }
                    catch (ProtocolException ex)
                    {
                        return Json(new { Success = false, ex.Message });
                    }
                }

                return Json(new { Success = false, Message = "Invalid identifier" });
            }
            return new EmptyResult();
        }

        public ActionResult Index(int? id)
        {
            if (id == null)
            {
                id = ExtendedIdentity.Id;
                ViewBag.IsOwner = true;
            }
            else
            {

                ViewBag.IsOwner = id == ExtendedIdentity.Id; 
            }
            var user = _userRepository.Find((int) id);
            return View(user);
        }

        public ActionResult LogOn()
        {
            return View();
        }

        [HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                User user;
                if (_userRepository.ValidateUser(model.Email, model.Password, out user))
                {
                    if (user.Status.Equals(Constant.StatusCreated))
                    {
                        return RedirectToAction("ActivationRequired");
                    }
                    if (user.IsBlocked)
                    {
                        ViewBag.Message = Constant.BlockedAccountMessage;
                        return View(model);
                    }
                    FormsAuthentication.SetAuthCookie(model.Email, model.RememberMe);

                    SessionPersiter.User = user;
                    if (user.IsAdmin)
                        return RedirectToAction("Index", "Admins");
                    if (IsValidReturnUrl(returnUrl))
                    {
                        return Redirect(returnUrl);
                    }
                   
                    return RedirectToAction("Index", "Home");
                }
                ViewBag.Message = Constant.IncorrectPasswordMessage;
            }
            return View(model);
        }

        public ActionResult LogonAjax(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                User user;
                if (_userRepository.ValidateUser(model.Email, model.Password, out user))
                {
                    FormsAuthentication.SetAuthCookie(user.Email, model.RememberMe);

                    SessionPersiter.User = user;
                    if (IsValidReturnUrl(returnUrl))
                    {
                        return Json(new { Success = true, returnUrl });
                    }
                    return user.Status.Equals(Constant.StatusCreated)
                               ? Json(new { Success = true, returnUrl = Url.Action("RequireActivation", "Account") })
                               : Json(new { Success = true, returnUrl = Url.Action("Index", "Home") });
                }
            }
            return Json(new { Success = false, returnUrl = Url.Action("LogOn", "Account") });
        }

        private bool IsValidReturnUrl(string returnUrl)
        {
            bool isValid = true;
            foreach (var path in Constant.NoReturnPaths)
            {
                if (returnUrl.ToLower().Contains(path))
                {
                    isValid = false;
                    break;
                }
            }
            return isValid && Url.IsLocalUrl(returnUrl) && !string.IsNullOrEmpty(returnUrl) && returnUrl.StartsWith("/")
                   && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\");
        }

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            SessionPersiter.User = null;
            return RedirectToAction("Index", "Home");
        }

        public ActionResult Register()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                var result = new User();
                var realHash = Request.Form["realHash"];
                var realInput = UserRepository.RpHash(model.RealPerson);
                if (!realInput.Equals(realHash))
                {
                    ModelState.AddModelError("RealPerson", Constant.RealPersonErrorMessage);
                }
                if (ModelState.IsValid)
                {
                    try
                    {
                        result = _userRepository.CreateInternalUser(model.UserName, model.DisplayName, model.Password, model.Email);
                        if (result != null)
                        {
                            _userMailer.Activate(result).Send();

                            _activityRepository.CreateActivity(result, Constant.RegisteredType);
                            _activityRepository.Save();
                            return RedirectToAction("RequireActivation");
                        }
                    }
                    catch (ArgumentException e)
                    {
                        ModelState.AddModelError(e.ParamName, e.Message.Remove(e.Message.IndexOf("Parameter")));
                    }

                }
            }
            return View(model);
        }

        public ActionResult Activate()
        {
            var userId = Request.QueryString["userid"];
            var validationCode = Request.QueryString["code"];
            if (!String.IsNullOrEmpty(userId) && !String.IsNullOrEmpty(validationCode))
            {
                try
                {
                    var id = Int32.Parse(userId);
                    if (_userRepository.ValidateActivationCode(id, validationCode))
                    {
                        var user = _userRepository.Find(id);
                        user.Status = Constant.StatusApproved;
                        FormsAuthentication.SetAuthCookie(user.Email, false);
                        SessionPersiter.User = user;
                        return RedirectToAction("ActivateSuccess", "Account");
                    }
                }
                catch (Exception)
                {
                    return View();
                }
            }
            return View();
        }

        public ActionResult Reactivate()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Reactivate(RequireEmailModel model)
        {
            string realHash = Request.Form["realHash"];
            string realInput = UserRepository.RpHash(model.RealPerson);
            if (!realInput.Equals(realHash))
            {
                ModelState.AddModelError("RealPerson", Constant.RealPersonErrorMessage);
            }
            if (ModelState.IsValid)
            {
                User user = _userRepository.Reactivate(model.Email);
                if (user != null)
                {
                    _userMailer.Activate(user).Send();
                    return RedirectToAction("ActivationSent", "Account");
                }
                ModelState.AddModelError("Email", Constant.EmailInvalidErrorMessage);
            }
            return View(model);
        }

        [ExtendedAuthentication]
        public ActionResult ChangePassword()
        {
            return View();
        }

        [ExtendedAuthentication, HttpPost]
        public ActionResult ChangePassword(ChangePasswordModel model)
        {
            var realHash = Request.Form["realHash"];
            var realInput = UserRepository.RpHash(model.RealPerson);
            if (!realInput.Equals(realHash))
            {
                ModelState.AddModelError("RealPerson", Constant.RealPersonErrorMessage);
            }

            if (ModelState.IsValid)
            {
                var changePasswordSucceeded = false;
                try
                {
                    HttpCookie cookie = HttpContext.Request.Cookies.Get(FormsAuthentication.FormsCookieName);
                    if (cookie != null)
                    {
                        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
                        string email = ticket.Name;
                        changePasswordSucceeded = _userRepository.ChangePassword(email, model.OldPassword, model.NewPassword);
                    }
                }
                catch (Exception)
                {
                    changePasswordSucceeded = false;
                }

                if (changePasswordSucceeded)
                {
                    FormsAuthentication.SignOut();
                    SessionPersiter.User = null;
                    return RedirectToAction("ChangePasswordSuccess");
                }
                ModelState.AddModelError(string.Empty, Constant.PasswordErrorMessage);
            }

            return View(model);
        }

        public ActionResult RequestResetPassword()
        {
            return View();
        }

        [HttpPost]
        public ActionResult RequestResetPassword(RequireEmailModel model)
        {
            if (ModelState.IsValid)
            {
                User user = _userRepository.RequestResetPassword(model.Email);
                if (user != null)
                {
                    _userMailer.ResetPassword(user).Send();
                    return RedirectToAction("ResetPasswordSent");
                }
            }
            return View(model);
        }

        public ActionResult ResetPassword()
        {
            string userId = Request.QueryString["userid"];
            if (!String.IsNullOrEmpty(userId))
            {
                try
                {
                    User user = _userRepository.Find(Int32.Parse(userId));
                    ViewBag.DisplayName = user.DisplayName;
                }
                catch (Exception)
                {
                    return RedirectToAction("Index", "Home");
                }
            }
            return View();
        }

        [HttpPost]
        public ActionResult ResetPassword(ResetPasswordModel model)
        {
            string userId = Request.Form["userId"];
            string validationCode = Request.Form["validateCode"];
            try
            {
                int id = Int32.Parse(userId);
                if (_userRepository.ValidateNewPassword(id, validationCode, model.NewPassword))
                {
                    return RedirectToAction("ResetPasswordSuccess");
                }
            }
            catch (Exception)
            {
                return View(model);
            }
            return View(model);
        }

        //TODO: Dinh to fix later
        [HttpPost]
        public ActionResult UserValidation(RegisterModel model)
        {
            if (!String.IsNullOrEmpty(model.UserName))
            {
                var reg = new Regex(@"^[a-zA-Z0-9_-_.]{3,20}$");
                if (reg.IsMatch(model.UserName))
                {
                    User user = _userRepository.Find(model.UserName);
                    if (user != null)
                    {
                        ModelState.AddModelError("UserName", Constant.UsernameExistedErrorMessage);
                        return Json(new
                        {
                            Valid = false,
                            Message = Constant.UsernameExistedErrorMessage,
                        });
                    }
                    return Json(new
                    {
                        Valid = true
                    });
                }
                ModelState.AddModelError("UserName", Constant.UserInvalidErrorMessage);
                return Json(new
                                {
                                    Valid = false,
                                    Message = Constant.UserInvalidErrorMessage
                                });
            }
            ModelState.AddModelError("UserName", Constant.UsernameMissingErrorMessage);
            return Json(new
                            {
                                Valid = false,
                                Message = Constant.UsernameMissingErrorMessage
                            });
        }
        //TODO: Dinh to fix later
        [HttpPost]
        public ActionResult EmailValidation(RegisterModel model)
        {
            if (!String.IsNullOrEmpty(model.Email))
            {
                var regex =
                    new Regex(
                        @"^[\w-]+(\.[\w-]+)*@([a-z0-9-]+(\.[a-z0-9-]+)*?\.[a-z]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?$");
                if (!regex.IsMatch(model.Email))
                {
                    ModelState.AddModelError("Email", "Your email address is invalid.");
                    return Json(new
                                    {
                                        Valid = false,
                                        Message = "Your email address is invalid."
                                    });
                }
                IQueryable<User> users = _userRepository.All;
                foreach (User user in users)
                {
                    if (model.Email.Equals(user.Email))
                    {
                        ModelState.AddModelError("Email", "Email already exists.");
                        return Json(new
                                        {
                                            Valid = false,
                                            Message = "Email already exists."
                                        });
                    }
                }
                return Json(new
                                {
                                    Valid = true
                                });
            }

            ModelState.AddModelError("Email", "Email is required.");
            return Json(new
                            {
                                Valid = false,
                                Message = "Email is required."
                            });
        }

        public ActionResult ChangePasswordSuccess()
        {
            return View();
        }

        public ActionResult RequireActivation()
        {
            return View();
        }

        public ActionResult ActivationSent()
        {
            return View();
        }

        public ActionResult ActivateSuccess()
        {
            return View();
        }

        public ActionResult ResetPasswordSent()
        {
            return View();
        }

        public ActionResult ResetPasswordSuccess()
        {
            return View();
        }

        [ExtendedAuthentication]
        public ActionResult Details()
        {
            User user = _userRepository.Find(ExtendedIdentity.Id);
            return View(user);
        }

        [ExtendedAuthentication]
        public ActionResult SaveProfile(User user)
        {
            if (user != null)
            {
                _userRepository.UpdateProfile(user);
                _userRepository.Save();
            }
            return RedirectToAction("Index");
        }
        
        [JsonAuthentication]
        public ActionResult UploadAvatar([ModelBinder(typeof(ImageUploadModelBinder))] dynamic fileInfo)
        {
            try
            {
                var avatarUrl = new StringBuilder();
                HttpPostedFileBase fileData = fileInfo.FileData;
                var filename = DateTime.Now.ToString("dd_MM_yyyy_HH_mm_ss") + "_" +
                                              Path.GetFileName(fileData.FileName);
                if (string.IsNullOrEmpty(filename))
                {
                    return Json(new { Success = false, Message = "Upload failed. Please try again later." });
                }
                var savedFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Constant.SaveAvatarPath,
                                                    filename);
                if (!Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Constant.SaveAvatarPath)))
                {
                    Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
                                                           Constant.SaveAvatarPath));
                }

                // Save new avatar
                fileData.SaveAs(savedFilename);

                var user = _userRepository.Find(ExtendedIdentity.Id);
                var oldAvatar = user.AvatarUrl;

                avatarUrl.Append("/");
                avatarUrl.Append(Constant.SaveAvatarPath);
                avatarUrl.Append("/");
                avatarUrl.Append(filename);

                _userRepository.UpdateAvatar(user, avatarUrl.ToString());
                SessionPersiter.User = null;
                // Delete old avatar
                if (!oldAvatar.Equals(Constant.DefaultAvatarUrl))
                {
                    oldAvatar = oldAvatar.Remove(0, 1).Replace("/", "\\");
                    var avatarInfo = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, oldAvatar));
                    if (avatarInfo.Exists)
                    {
                        avatarInfo.Delete();
                    }
                }

                return Json(new { Success = true, AvatarUrl = avatarUrl.ToString() });
            }
            catch (Exception)
            {
                return Json(new { Success = false, Message = "Upload failed. Please try again later." });
            }
        }

        public ActionResult Activities(int? id)
        {
            var activities = (id == null)
                                 ? _activityRepository.FindByUser(ExtendedIdentity.Id)
                                 : _activityRepository.FindByUser((int) id);
            var activity = activities.FirstOrDefault();
            if (activity != null)
            {
                var user = _userRepository.Find(activity.UserId);
                ViewBag.DisplayName = user.DisplayName;
                ViewBag.AvatarUrl = user.AvatarUrl;
                ViewBag.Location = user.Location;
                ViewBag.IsOwner = id == null;
            }
            return View(activities);
        }

        public ActionResult FetchNextBookPage(int userId, int pageNumber)
        {
            var listBooks = _userRepository.FetchNextBookPage(userId, pageNumber).ToList();
            return Json(listBooks.ListBookToJsonResult());
        }

        public ActionResult FetchNextActivitiesPage(int userId, int pageNumber)
        {
            List<Activity> listActivities = _userRepository.FetchNextActivitiesPage(userId, pageNumber).ToList();
            return Json(new
                            {
                                isSuccess = true,
                                activities = listActivities.Select(a => new
                                                                            {
                                                                                id = a.Id,
                                                                                userId = a.UserId,
                                                                                description = a.Description,
                                                                                createdDateString =
                                                                            Helpers.Helpers.ConvertDateToString(
                                                                                a.CreatedDate),
                                                                                type = a.Type,
                                                                                referId = a.ReferId
                                                                            })
                            });
        }

        public ActionResult AccessDenied()
        {
            return View();
        }

        public ActionResult ActivationRequired()
        {
            return View();
        }

        [ExtendedAuthentication]
        public ActionResult ReportUser(int id)
        {
            var feedback = _userRepository.ReportUser(ExtendedIdentity.Id, id);
            if (feedback.IsSuccess)
            {
                return SuccessFeedback;
            }
            return Json(new {success = false, message = feedback.Message});
        }
    }
}
